package crypto

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"crypto/rand"

	"golang.org/x/crypto/blowfish"
	"golang.org/x/crypto/cast5"
	"golang.org/x/crypto/tea"
	"golang.org/x/crypto/twofish"
	"golang.org/x/crypto/xtea"
	"golang.org/x/crypto/xts"
)

func NewBc() *bc {
	key := make([]byte, aes.BlockSize*2)
	key2 := make([]byte, xtea.BlockSize*2)

	_, err := rand.Read(key)
	if err != nil {
		panic(err)
	}
	_, err = rand.Read(key2)
	if err != nil {
		panic(err)
	}

	return &bc{
		key:   key,
		iv:    nil,
		delta: 0x9e3779b9,
		key2:  key2,
	}
}

type bc struct {
	key    []byte //
	iv     []byte
	round  int    // tea 奇数
	delta  uint32 // tea 自实现
	sector uint64 // Xts
	key2   []byte // Xtea
}

func (r *bc) Key(key []byte) *bc {
	r.key = key

	return r
}
func (r *bc) Iv(iv []byte) *bc {
	r.iv = iv

	return r
}
func (r *bc) Round(round int) *bc {
	if round&1 == 0 {
		r.round = round
	}

	return r
}
func (r *bc) Sector(sector uint64) *bc {
	r.sector = sector

	return r
}
func (r *bc) Key2(key2 []byte) *bc {
	r.key2 = key2

	return r
}

// Des
// key5 8
// iv =blockSize
func (r *bc) Des(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipherBlock, err := des.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	source = pad(source, cipherBlock.BlockSize())

	encrypter := cipher.NewCFBEncrypter(cipherBlock, r.iv)
	encrypter.XORKeyStream(encrypted, source)

	return encrypted
}
func (r *bc) DesDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := des.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	decrypter := cipher.NewCFBDecrypter(cipherBlock, r.iv)
	decrypter.XORKeyStream(decrypted, source)

	decrypted = unpad(decrypted)

	return decrypted
}

// Aes
// key5 16|24|32
// iv =blockSize
func (r *bc) Aes(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipherBlock, err := aes.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	encrypter := cipher.NewCBCEncrypter(cipherBlock, r.iv)
	encrypter.CryptBlocks(encrypted, source)
	// encrypter := cipher.NewCFBEncrypter(aesCipher, r.iv)
	// encrypter.XORKeyStream(encrypted, source)
	// stream := cipher.NewCTR(aesCipher, r.iv)
	// stream := cipher.NewOFB(aesCipher, r.iv)
	// aead, err := cipher.NewGCM(aesCipher)
	// if err != nil {
	// 	panic(err)
	// }
	// fmt.Println(aead)
	// aead, err = cipher.NewGCMWithNonceSize(aesCipher, 12)
	// if err != nil {
	// 	panic(err)
	// }
	// fmt.Println(aead)
	// aead, err = cipher.NewGCMWithTagSize(aesCipher, 12)
	// if err != nil {
	// 	panic(err)
	// }
	// fmt.Println(aead)

	return encrypted
}
func (r *bc) AesDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := aes.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	decrypter := cipher.NewCBCDecrypter(cipherBlock, r.iv)
	decrypter.CryptBlocks(decrypted, source)
	// decrypter := cipher.NewCFBDecrypter(aesCipher, r.iv)
	// decrypter.XORKeyStream(decrypted, source)

	return decrypted
}

// Blowfish
// key5 1<=k<=56
// iv =blockSize
func (r *bc) Blowfish(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipherBlock, err := blowfish.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cfb := cipher.NewCFBEncrypter(cipherBlock, r.iv) // 密码反馈模式
	cfb.XORKeyStream(encrypted, source)

	return encrypted
}
func (r *bc) BlowfishDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := blowfish.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cfb := cipher.NewCFBDecrypter(cipherBlock, r.iv)
	cfb.XORKeyStream(decrypted, source)

	return decrypted
}

// Twofish
// key5 16|24|32
// iv =blockSize
func (r *bc) Twofish(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipherBlock, err := twofish.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cfb := cipher.NewCFBEncrypter(cipherBlock, r.iv)
	cfb.XORKeyStream(encrypted, source)

	return encrypted
}
func (r *bc) TwofishDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := twofish.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cfb := cipher.NewCFBDecrypter(cipherBlock, r.iv)
	cfb.XORKeyStream(decrypted, source)

	return decrypted
}

// Tea
// key5
// round
func (r *bc) Tea(source []byte) []byte { // key5 16 位
	encrypted := make([]byte, len(source))

	// cipherBlock, err := tea.NewCipher(r.key5)
	cipherBlock, err := tea.NewCipherWithRounds(r.key, r.round)
	if err != nil {
		panic(err)
	}

	cipherBlock.Encrypt(encrypted, source)

	return encrypted
}
func (r *bc) TeaDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	// cipherBlock, err := tea.NewCipher(r.key5)
	cipherBlock, err := tea.NewCipherWithRounds(r.key, r.round)
	if err != nil {
		panic(err)
	}

	cipherBlock.Decrypt(decrypted, source)

	return decrypted
}

// Tea2 自实现
func (r *bc) Tea2(v [2]uint32, key [4]uint32) [2]uint32 {
	sum := uint32(0)
	k0, k1, k2, k3 := key[0], key[1], key[2], key[3]

	for i := 0; i < 32; i++ {
		sum += r.delta
		v[0] += ((v[1] << 4) + k0) ^ (v[1] + sum) ^ ((v[1] >> 5) + k1)
		v[1] += ((v[0] << 4) + k2) ^ (v[0] + sum) ^ ((v[0] >> 5) + k3)
	}

	return v
}
func (r *bc) TeaDecrypt2(v [2]uint32, key [4]uint32) [2]uint32 {
	sum := r.delta * 32
	k0, k1, k2, k3 := key[0], key[1], key[2], key[3]

	for i := 0; i < 32; i++ {
		v[1] -= ((v[0] << 4) + k2) ^ (v[0] + sum) ^ ((v[0] >> 5) + k3)
		v[0] -= ((v[1] << 4) + k0) ^ (v[1] + sum) ^ ((v[1] >> 5) + k1)
		sum -= r.delta
	}

	return v
}

// Xtea
// key5 16 key2
func (r *bc) Xtea(source []byte) []byte { // key5 16位
	encrypted := make([]byte, len(source))

	cipherBlock, err := xtea.NewCipher(r.key2)
	if err != nil {
		panic(err)
	}

	cipherBlock.Encrypt(encrypted, source)

	return encrypted
}
func (r *bc) XteaDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := xtea.NewCipher(r.key2)
	if err != nil {
		panic(err)
	}

	cipherBlock.Decrypt(decrypted, source)

	return decrypted
}

// Cast5 blockSize=8
// key5 16
func (r *bc) Cast5(source []byte) []byte { // cast5.KeySize 16
	encrypted := make([]byte, len(source))

	cipherBlock, err := cast5.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cipherBlock.Encrypt(encrypted, source)

	return encrypted
}
func (r *bc) Cast5Decrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := cast5.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cipherBlock.Decrypt(decrypted, source)

	return decrypted
}

// Xts blockSize=16
// key5 =blockSize
// sector uint64
func (r *bc) Xts(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipherBlock, err := xts.NewCipher(aes.NewCipher, r.key)
	if err != nil {
		panic(err)
	}

	cipherBlock.Encrypt(encrypted, source, r.sector)

	return encrypted
}
func (r *bc) XtsDecrypt(source []byte) []byte {
	decrypted := make([]byte, len(source))

	cipherBlock, err := xts.NewCipher(aes.NewCipher, r.key)
	if err != nil {
		panic(err)
	}

	cipherBlock.Decrypt(decrypted, source, r.sector)

	return decrypted
}
